package position

import (
	b "code.google.com/p/gochess/board"
	"fmt"
)

type Castling struct {
	Color  Color
	Option CastlingOption
	KingLocation,
	KingDestination,
	KingMoveDirection,
	RookLocation,
	RookDestination b.Square
}

// Castlings holds the informations for castlings.
var Castlings = [...][numberOfCastlings - 1]Castling{
	White: {
		CastlingShort: Castling{White, CastlingShort, b.E1, b.G1, b.Right, b.H1, b.F1},
		CastlingLong:  Castling{White, CastlingLong, b.E1, b.C1, b.Left, b.A1, b.D1},
	},
	Black: {
		CastlingShort: Castling{Black, CastlingShort, b.E8, b.G8, b.Right, b.H8, b.F8},
		CastlingLong:  Castling{Black, CastlingLong, b.E8, b.C8, b.Left, b.A8, b.D8},
	},
}

/*
start: 1-8 bit
destination: 9-16 bit
promoteTo : 17-19 bit
castling 20-21 bit
en passant: 22 bit
*/

// Move represents a move of a piece on the board.
type Move int32

// NoMove represents the zero value for Move.
const NoMove Move = 0x00

const (
	offsetDestination = b.BitSizeSquare
	offsetPromotion   = offsetDestination + b.BitSizeSquare
	offsetCastling    = offsetPromotion + BitSizePieceType
	offsetIsEnPassant = offsetCastling + bitSizeCastlingOption
)

const (
	BitSizeMove    = offsetIsEnPassant + 1
	BitMaskMove    = 1<<BitSizeMove - 1
	bitMaskSpecial = BitMaskPieceType<<offsetPromotion |
		bitMaskCastlingOption<<offsetCastling |
		b.BitMaskSquare<<offsetIsEnPassant
)

// Start returns the departure square of the move.
// In case of a castling the departure square of the rook move is returned.
func (this Move) Start() b.Square {
	return b.Square(this & b.BitMaskSquare)
}

// Destination returns the arrival square of the move.
// In case of a castling the arrival square of the rook move is returned.

// bit 1: is special
// 2-3:
// 00: promotion
// 01: castling
// 10: ep
// 11: pawn double push?
// 4-8:
// promotion_
// 4-6: promoted to
// 7-8: move direction: left (up/down)+b.Square(7-8)
// castling:
// 4: castling type
// ep:
// 4-5: move direction: left (up/down)+b.Square(4,5)
func (this Move) Destination() b.Square {
	return b.Square(this >> offsetDestination & b.BitMaskSquare)
}

// IsSpecial returns true if the move is a promotion or a castling or
// a en passant capture.
func (this Move) IsSpecial() bool {
	return this&bitMaskSpecial != 0
}

// Promotion returns the piece type to which a pawn was promoted.
// In case of a non promotion move NoPieceType is returned.
func (this Move) Promotion() PieceType {
	return PieceType(this >> offsetPromotion & BitMaskPieceType)
}

// Castling returns the castling type of a castling move.
// If move is not a castling NoCastling is returned.
func (this Move) Castling() CastlingOption {
	return CastlingOption(this >> offsetCastling & bitMaskCastlingOption)
}

// CapturesEnPassant returns true if the move is an en passant capture.
func (this Move) CapturesEnPassant() bool {
	return this>>offsetIsEnPassant == 1
}

func (this Move) String() string {

	if this == NoMove {
		return "{NoMove}"
	}
	var special string
	switch {
	case this.Promotion() != NoPieceType:
		special = fmt.Sprintf("(%v)", this.Promotion())
	case this.Castling() != NoCastling:
		special = fmt.Sprintf(" %v", this.Castling())
	case this.CapturesEnPassant():
		special = fmt.Sprint(" ep")
	}
	return fmt.Sprintf("{%s-%s%s}",
		this.Start(), this.Destination(), special)
}

// NewMove creates a new move from start to dst.
func NewMove(start, dst b.Square) Move {
	return Move(start) | Move(dst)<<offsetDestination
}

// NewPromotion returns a promotion move to the given promotion piece type.
func NewPromotion(start, dst b.Square, promotion PieceType) Move {
	return NewMove(start, dst) | Move(promotion)<<offsetPromotion
}

// NewCastling created a castling move for the given color and castling option.
func NewCastling(color Color, castling CastlingOption) Move {
	castlingInfo := Castlings[color][castling]
	// we use the rook as we can use it for check detection later
	// the king move is irrelevant in this case
	return NewMove(castlingInfo.RookLocation, castlingInfo.RookDestination) |
		Move(castling)<<offsetCastling
}

// NewEnPassantCapture returns an en passant capture.
func NewEnPassant(start, dst b.Square) Move {
	return NewMove(start, dst) | Move(1)<<offsetIsEnPassant
}
